#include "coro_tcp.h"
#include <cstring>
#include <cerrno>
#include "../util/logger.h"
#include <cerrno>

namespace wilson::net
{
    ::std::error_code shutdown_for_writing = { 
        EPIPE, ::std::system_category()
    };

    send_aw::send_aw(
        coro::epoll_suspend_manager& esm,
        tcp_io& io, 
        ::std::span<const ::std::byte> buffer, 
        int flags, 
        ::std::error_code* ec
    ) : tcp_io_base{ esm, io, flags, ec }, 
        ia_buffer{ buffer }
    {
    }

    recv_aw::recv_aw(
        coro::epoll_suspend_manager& esm,
        tcp_io& io, 
        ::std::span<::std::byte> buffer, 
        int flags, 
        ::std::error_code* ec
    ) : tcp_io_base{ esm, io, flags, ec }, 
        ia_buffer{ buffer }
    {
    }

    sendmsg_aw::sendmsg_aw(
        coro::epoll_suspend_manager& esm,
        tcp_io& io, 
        const msghdr* msg, 
        int flags, 
        ::std::error_code* ec
    ) : tcp_io_base{ esm, io, flags, ec }, 
        ia_buffer{ msg }
    {
    }

    recvmsg_aw::recvmsg_aw(
        coro::epoll_suspend_manager& esm,
        tcp_io& io, 
        msghdr* msg, 
        int flags, 
        ::std::error_code* ec
    ) : tcp_io_base{ esm, io, flags, ec }, 
        ia_buffer{ msg }
    {
    }

    bool send_file_aw::await_ready() noexcept
    {
        ssize_t ret = ia_io.send(m_fd, m_file_size, ia_flags);
        if (ret == -1)
        {
            if (errno == EAGAIN) return false;
        }
        ia_result = ret;
        return true;
    }

    void send_file_aw::await_suspend(::std::coroutine_handle<> h)
    {
        ia_epoll_susp_mgr.add_task(ia_io.fd(), EPOLLOUT, h);
    }

    size_t send_file_aw::await_resume()
    {
        ssize_t ret{};
        ::std::string_view errmsg;
        switch (static_cast<io_state>(ia_result))
        {
        case io_state::INIT:
            ret = ia_io.send(m_fd, m_file_size, ia_flags);                             
            if (ret == -1)                                                
            {
                [[fallthrough]];
            }
            else break;
        case io_state::ERR: 
            ret = 0;
            errmsg =  { ::strerror(errno) };
            if (ia_ec) *ia_ec = ::std::error_code{ errno, ::std::system_category() };
            else 
            {
                WILSON_LOG_DEBUG(errmsg);
                throw ::std::runtime_error{ errmsg.data() };
            }
            break;
        default: ret = ia_result;
        }
        return static_cast<size_t>(ret);
    }

    bool send_aw::await_ready() noexcept
    {
        ssize_t ret = ia_io.send(ia_buffer, ia_flags);
        if (ret == -1)
        {
            if (errno == EAGAIN) return false;
        }
        ia_result = ret;
        return true;
    }

    void send_aw::await_suspend(::std::coroutine_handle<> h)
    {
        ia_epoll_susp_mgr.add_task(ia_io.fd(), EPOLLOUT, h);
    }

    size_t send_aw::await_resume()
    {
        ssize_t ret{};
        ::std::string_view errmsg;
        switch (static_cast<io_state>(ia_result))
        {
        case io_state::INIT:
            ret = ia_io.send(ia_buffer, ia_flags);                             
            if (ret == -1)                                                
            {
                [[fallthrough]];
            }
            else break;
        case io_state::ERR: 
            ret = 0;
            errmsg =  { ::strerror(errno) };
            if (ia_ec) *ia_ec = ::std::error_code{ errno, ::std::system_category() };
            else 
            {
                WILSON_LOG_DEBUG(errmsg);
                throw ::std::runtime_error{ errmsg.data() };
            }
            break;
        default: ret = ia_result;
        }
        return static_cast<size_t>(ret);
    }

    bool recv_aw::await_ready() noexcept
    {
        ssize_t ret = ia_io.recv(ia_buffer, ia_flags);
        if (ret == -1)
        {
            if (errno == EAGAIN) return false;
        }
        ia_result = ret;
        return true;
    }

    void recv_aw::await_suspend(::std::coroutine_handle<> h)
    {
        ia_epoll_susp_mgr.add_task(ia_io.fd(), EPOLLIN, h);
    }

    size_t recv_aw::await_resume()
    {
        ssize_t ret{};
        ::std::string_view errmsg;
        switch (static_cast<io_state>(ia_result))
        {
        case io_state::INIT:
            ret = ia_io.recv(ia_buffer, ia_flags);                             
            if (ret == -1)                                                
            {
                [[fallthrough]];
            }
            else break;
        case io_state::ERR: 
            ret = 0;
            errmsg =  { ::strerror(errno) };
            if (ia_ec) *ia_ec = ::std::error_code{ errno, ::std::system_category() };
            else 
            {
                WILSON_LOG_DEBUG(errmsg);
                throw ::std::runtime_error{ ::strerror(errno) };
            }
            break;
        default: ret = ia_result;
        }
        return static_cast<size_t>(ret);
    }

    bool sendmsg_aw::await_ready() noexcept
    {
        ssize_t ret = ia_io.sendmsg(ia_buffer, ia_flags);
        if (ret == -1)
        {
            if (errno == EAGAIN) return false;
        }
        ia_result = ret;
        return true;
    }

    void sendmsg_aw::await_suspend(::std::coroutine_handle<> h)
    {
        ia_epoll_susp_mgr.add_task(ia_io.fd(), EPOLLOUT, h);
    }

    size_t sendmsg_aw::await_resume()
    {
        ssize_t ret{};
        ::std::string_view errmsg;
        switch (static_cast<io_state>(ia_result))
        {
        case io_state::INIT:
            ret = ia_io.sendmsg(ia_buffer, ia_flags);                             
            if (ret == -1)                                                
            {
                [[fallthrough]];
            }
            else break;
        case io_state::ERR: 
            ret = 0;
            errmsg =  { ::strerror(errno) };
            if (ia_ec) *ia_ec = ::std::error_code{ errno, ::std::system_category() };
            else 
            {
                WILSON_LOG_DEBUG(errmsg);
                throw ::std::runtime_error{ ::strerror(errno) };
            }
            break;
        default: ret = ia_result;
        }
        return static_cast<size_t>(ret);
    }

    bool recvmsg_aw::await_ready() noexcept
    {
        ssize_t ret = ia_io.recvmsg(ia_buffer, ia_flags);
        if (ret == -1)
        {
            if (errno == EAGAIN) return false;
        }
        ia_result = ret;
        return true;
    }

    void recvmsg_aw::await_suspend(::std::coroutine_handle<> h)
    {
        ia_epoll_susp_mgr.add_task(ia_io.fd(), EPOLLIN, h);
    }

    size_t recvmsg_aw::await_resume()
    {
        ssize_t ret{};
        ::std::string_view errmsg;
        switch (static_cast<io_state>(ia_result))
        {
        case io_state::INIT:
            ret = ia_io.recvmsg(ia_buffer, ia_flags);                             
            if (ret == -1)                                                
            {
                [[fallthrough]];
            }
            else break;
        case io_state::ERR: 
            ret = 0;
            errmsg =  { ::strerror(errno) };
            if (ia_ec) *ia_ec = ::std::error_code{ errno, ::std::system_category() };
            else 
            {
                WILSON_LOG_DEBUG(errmsg);
                throw ::std::runtime_error{ ::strerror(errno) };
            }
            break;
        default: ret = ia_result;
        }
        return static_cast<size_t>(ret);
    }
}
